#!/usr/bin/env python
from collections import deque
from config import *
import math

################################################################################
# Voice Activity Detection
################################################################################

class VoiceActivityDetector:

    def __init__(self):
        self.start()

    def update(self, L):
        cur_energy = self.calc_frame_energy(L)
        
        if self.measuring_noise == False:
            cur_energy_dist = self.calc_energy_dist(cur_energy)
            
            self.ed_buffer.append(cur_energy_dist)
            self.update_smoothed_energy_dist()
            
            self.prev_energy = cur_energy

        # Measure noise for a few frames to get the noise energy
        else:
            self.noise_buffer.append(cur_energy)
            if len(self.noise_buffer) >= NOISE_MEASURE_FRAMES:
                self.noise_energy = average(self.noise_buffer)
                self.measuring_noise = False
                # Automatically calculate threshold
                if self.threshold == None:
                    energy_dists = []
                    for energy in self.noise_buffer:
                        energy_dists.append(self.calc_energy_dist(energy))
                        self.prev_energy = energy
                    avg_energy_dist = average(energy_dists)
                    print '-------------------------------------------------------'
                    print "Average smoothed energy distance:", avg_energy_dist
                    self.threshold = avg_energy_dist*THRESHOLD_MULTIPLIER
                    print "VAD threshold:", self.threshold
                    print '-------------------------------------------------------'

    # Returns whether voice was detected FRAME_BUFFER_LEN/2 frames ago
    def is_talking(self):
        if self.threshold != None:
            return self.smoothed_energy_dist > self.threshold
        else:
            return False

    def calc_frame_energy(self, L):
        energy = 0
        for num in L:
            energy += num**2
        return float(energy)

    def calc_snr(self, cur_energy):
        return abs(math.log(cur_energy/self.noise_energy))

    def calc_energy_dist(self, cur_energy):                
        snr = self.calc_snr(cur_energy)
        return math.sqrt(abs(cur_energy - self.prev_energy) * snr)

    def update_smoothed_energy_dist(self):
        self.smoothed_energy_dist = average(self.ed_buffer)

    def start(self):
        self.measuring_noise = True
        self.noise_buffer = []
        self.ed_buffer = deque(maxlen=FRAME_BUFFER_LEN)
        self.noise_energy = 0.0
        self.smoothed_energy_dist = 0.0
        self.prev_energy = 0.0
        if THRESHOLD == None:
            self.threshold = None
        else:
            self.threshold = THRESHOLD

    def restart(self):
        self.ed_buffer = deque(maxlen=FRAME_BUFFER_LEN)
        self.smoothed_energy_dist = 0.0

def average(values):
    return sum(values)/len(values)
